package com.hornsun.jwt;

import com.google.common.base.Throwables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author 编译中、、
 * @date 2017/12/24
 */
public class JwtAuthenticationFilter extends GenericFilterBean {
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    @Resource
    private AuthenticationManager authenticationManager;

    @Override
    public void afterPropertiesSet() {
        Assert.notNull(this.authenticationManager, "authenticationManager must be specified");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (request.getRequestURI().startsWith("http")) {
            if (authentication == null) {
                String tokenPayload = request.getParameter(JwtToken.TOKEN);
                if (!StringUtils.isEmpty(tokenPayload)) {
                    RawAccessJwtToken token = new RawAccessJwtToken(tokenPayload);
                    Authentication jwtAuthentication = new JwtAuthenticationToken(token);
                    try {
                        jwtAuthentication = this.authenticationManager.authenticate(jwtAuthentication);
                        SecurityContextHolder.getContext().setAuthentication(jwtAuthentication);
                        logger.debug("SecurityContextHolder populated with jwt token: \'{}\'", SecurityContextHolder.getContext().getAuthentication());
                    } catch (AuthenticationException ex) {
                        if (ex instanceof JwtExpiredTokenException) {
                            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "TEST: JWT 已经过期");
                        } else if (ex instanceof JwtTokenInvalidException) {
                            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "TEST: JWT 无效");
                        }
                        logger.debug("SecurityContextHolder not populated with jwt token: \'{}\'; invalidating jwt token, {}", token, Throwables.getStackTraceAsString(ex));
                        return;
                    }
                }
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("SecurityContextHolder not populated with jwt token, as it already contained: \' {} \'", SecurityContextHolder.getContext().getAuthentication());
                }
            }
        }
        chain.doFilter(request, response);
    }

}